////////////////////////////////////////////////
// DESCRIPTION:
//     Class for device driver IRQ information
//     and manipulation
//
// Legal Notices:
//     Copyright (c) 2008, Telliam Consulting, LLC.
//     All rights reserved.
//
//     Redistribution and use in source and binary forms, with or without modification,
//     are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright notice,
//       this list of conditions and the following disclaimer in the documentation
//       and/or other materials provided with the distribution.
//
//     * Neither the name of Telliam Consulting nor the names of its contributors
//       may be used to endorse or promote products derived from this software
//       without specific prior written permission.
//
// Disclaimer:
//     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
//     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
//     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
//     IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
//     INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
//     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
//     DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//     LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
//     OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
//     OF THE POSSIBILITY OF SUCH DAMAGE.  
//
#pragma once

#include <FusionWare\Win32.h>

namespace FusionWare
{
    namespace DFX
    {
        ///////////////////////////////////////////////////////////////
        // Description:
        //   Class to manage reading and storing standard IRQ/ISR registry
        //   values for a device driver
        //
        // Remarks:
        //   Reads and stores standard IRQ/ISR registry values
        //   these include:
        //@table
        //  Name          Type    Description
        //  ---------     -----   -----------
        //  SysIntr       DWORD   Logical Interrupt ID for the device.
        //  IsrDll        SZ      Name of Installable ISR (If any)
        //  IsrHandler    SZ      Name of IsrHandler Function in installable DLL (If any)
        //  Irq           DWORD   Irq Number if assigned
        //  Priority256   DWORD   IST Priority
        //  EnableWake    DWORD   Bool to indicate if the device should Enable Wake on interrupt
        //  IrqMode       DWORD   Irq Mode for the installable ISR (EdgeTriggered/LevelTriggered)
        //  IrqState      DWORD   Irq State for the installable ISR (Active High/Active Low)
        //  IsrInfo       BINARY  Information to pass to the Installed ISR handler (required if IsrDll is set)
        //
        // Note:
        //   The defaults are set in the constructor and not in ReadRegValues
        //   to allow clients to override some or all of the defaults without the
        //   need for deriving a new class just to handle different default
        //   considerations.
        //
        class IrqInfo : protected FusionWare::Win32::RegValues
        {
        public:
            IrqInfo();
            virtual ~IrqInfo();
           
            // Remarks:
            //    useful wrapper around sequence of initializing an IRQ
            //    performs the following sequence of calls and handles
            //    failures at any stage:
            //         RequestSysIntr()
            //         LoadIntChainHandler()
            //         SetIrqMode()
            //         SysEnableWake()
            //
            bool InitIrq();

            // requests a SYSINTR for the IRQ if no SYSINTR provided
            // otherwise do nothing as the SYSINTR is assumed valid.
            //
            // Returns:
            //   TRUE  if SYSINTR value is valid (either from a request or it already was valid)
            //   FALSE if a SYSINTR request was needed but failed.
            //
            // Note:
            //   This function is Idempotent
            //
            bool RequestSysIntr();

            // releases  a SYSINTR previously allocated with 
            // otherwise do nothing as the SYSINTR is assumed valid.
            //
            // Returns:
            //   TRUE  if SYSINTR value is valid (either from a request or it already was valid)
            //   FALSE if a SYSINTR request was needed but failed.
            //
            // Note:
            //   This function is Idempotent
            //
            bool ReleaseSysIntr();

            // Set the IRQ mode specified by the registry settings
            // for the IRQ device ID.
            //
            // Note:
            //   While technically speaking this function is *not* Idempotent
            //   it's generally safe to call as many times as needed since the
            //   hardware is set up to the desired state each time. 
            //
            bool SetIrqMode()
            {
                 return DFX::SetIrqMode( this->IrqMode, this->IrqState,  this->DDKIsrInfo.dwIrq  );
            }

            // Clear the IRQ mode specified by the registry settings
            // for the IRQ device ID.
            // Note:
            //   While technically speaking this function is *not* Idempotent
            //   it's generally safe to call as many times as needed since the
            //   hardware is set up to the correct state each time. 
            //
            bool ClearIrqMode()
            {
                 return DFX::SetIrqMode( None, this->IrqState, this->DDKIsrInfo.dwIrq  );
            }

            void SetPriority256(DWORD NewVal) { ASSERT(NewVal<256); this->Priority256=NewVal; }
            DWORD GetPriority256() const      { return this->Priority256; }

            ///////////////////////////////////////////////////////
            // Description: 
            //    Enables or disables the Interrupt source for wake
            //
            // Returns: 
            //     FALSE   The EnableWake registry setting was non zero but the request failed
            //     TRUE    All other cases
            //     
            // Note:
            //   While technically speaking this function is *not* Idempotent
            //   it's generally safe to call as many times as needed since the
            //   hardware and OAL are set up to the correct state each time. 
            //
            bool SysEnableWake(bool Enable=true);
            
            // Note:
            //   This function is Idempotent
            //
            bool LoadIntChainHandler();

            int GetIrq() const                 { return this->DDKIsrInfo.dwIrq; }
            UINT32 GetSysIntr() const          { return this->DDKIsrInfo.dwSysintr; }
            const WCHAR* GetIsrDll() const     { return this->DDKIsrInfo.szIsrDll; }
            const WCHAR* GetIsrHandler() const { return this->DDKIsrInfo.szIsrHandler; }

            ///////////////////////////////////////////////////////
            // Description: 
            //    Initializes the IRQ support and starts the IST.
            //
            // Input: 
            //    Func            - Function to run as the thread
            //    DefaultPriority - Default Priority for the thread if
            //                      none was provided in the registry.
            //
            // Remarks: 
            //    This function simplifies the set up and use of an IST.
            //    Since all the information about the IRQ is contained
            //    in an IrqInfo Instance it is able to do all of the
            //    initialization work and start the thread with a single
            //    call. 
            //
            // Note: 
            //    The thread is automatically stopped if it's running and
            //    all IRQ initialization released when the destructor runs.
            //    
            bool StartIST(FusionWare::Threads::IntSvcThread::IntSvcThreadFunction Func, DWORD DefaultPriority);

            ///////////////////////////////////////////////////////
            // Description: 
            //     Stops the IST if it's running
            //
            // Note: 
            //    The thread is automatically stopped if it's running and
            //    all IRQ initialization released when the destructor runs.
            //
            // Note:
            //   This function is Idempotent
            //
            bool StopIST();

            virtual bool ReadRegValues(FusionWare::Win32::RegKey& RootKey);
            
            // REVIEW: consider Init function to allow direct init without registry

            BEGIN_REG_VALUE_TABLE(IrqInfo)
                REGVAL_DWORD(_T("Irq"),         DDKIsrInfo.dwIrq)
                REGVAL_DWORD(_T("SysIntr"),     DDKIsrInfo.dwSysintr)
                REGVAL_STR(_T("IsrDll"),        DDKIsrInfo.szIsrDll, DEVDLL_LEN )
                REGVAL_STR(_T("IsrHandler"),    DDKIsrInfo.szIsrHandler, DEVENTRY_LEN)
                REGVAL_DWORD(_T("EnableWake"),  EnableWake)
                REGVAL_DWORD(_T("IrqMode"),     IrqMode)
                REGVAL_DWORD(_T("IrqState"),    IrqState)
                REGVAL_DWORD(_T("Priority256"), Priority256)
            END_REG_VALUE_TABLE

        protected:
            DDKISRINFO DDKIsrInfo; // Standard ISR info
            void* pIsrInfo;        // raw binary data to send to ISR handler 
            DWORD IsrInfoSize;     // size of data pointed to by pIsrInfo
            IntMode IrqMode;       // Irq Mode for the interrupt
            BOOL IrqState;         // State for the Irq Mode
            BOOL EnableWake;       // indicates if the IRQ should be set up to wake from sleep
            DWORD Priority256;     // Priority for the IST associated with this Irq

            bool SysIntrRequested; // true if SYSINTR was requested from OAL and neds to be released.
            FusionWare::DFX::ISRChainHandler ChainHandler; // Instalable ISR object (if any IISR specified)
            FusionWare::Threads::IntSvcThread ISTObj;      // Thread object for controlling IST

        private:
            // TODO: implement copy and assignement semantics
            IrqInfo(const IrqInfo&){}
            operator=(const IrqInfo&){}
        };
    }
}